package com.apes.fn.scm.lossSale;

import com.apes.crm.business.model.InsuranceCompanyDetail;
import com.apes.fn.business.fixedLoss.modle.LossQuotation;
import com.apes.fn.business.fixedLoss.service.FixedLossToForeign;
import com.apes.fn.scm.insuranceRules.InsuranceRule;
import com.apes.fn.scm.insuranceRules.InsuranceRuleService;
import com.apes.fn.scm.retailOrder.model.RetailOrder;
import com.apes.fn.scm.retailOrder.repository.RetailOrderRepository;
import com.apes.framework.api.DomainService;
import com.apes.framework.api.SpringManager;
import com.apes.framework.jpa.spec.JpaDsl;
import com.apes.framework.plugin.esb.api.SimpleRequest;
import com.apes.framework.util.MapUtil;
import com.apes.framework.util.ReflectUtil;
import com.apes.scm.account.account.model.AccountingMethod;
import com.apes.scm.account.account.model.InventoryBatch;
import com.apes.scm.account.account.model.InventoryMode;
import com.apes.scm.account.account.service.InventoryService;
import com.apes.scm.account.tax.model.Tax;
import com.apes.scm.account.tax.service.TaxService;
import com.apes.scm.masterdata.frame.model.Dept;
import com.apes.scm.masterdata.frame.repository.DeptRepository;
import com.apes.scm.masterdata.party.model.PartyRole;
import com.apes.scm.sal.saleOrder.SaleOrder;
import com.apes.scm.sal.saleOrder.SaleOrderRepository;
import com.apes.scm.voucher.model.Voucher;
import com.apes.scm.voucher.model.VoucherInventory;
import com.apes.scm.voucher.model.VoucherItem;
import com.apes.scm.voucher.repository.VoucherInventoryRepository;
import com.apes.scm.voucher.repository.VoucherRepository;
import com.apes.scm.voucher.service.BusinessType;
import com.apes.scm.voucher.service.VoucherType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * 按照商品退回成本，以及按照商品销售
 *
 * @author wuq
 * @Time 2023-7-24 10:16
 * @Description
 */
@Service
public class InsuranceSaleCostReturnService extends DomainService {

    @Autowired
    private SaleOrderRepository saleOrderRepository;
    @Autowired
    private VoucherRepository voucherRepository;
    @Autowired
    private VoucherInventoryRepository voucherInventoryRepository;
    @Autowired
    private InventoryService inventoryService;
    @Autowired
    private TaxService taxService;

    private void test(SimpleRequest request){
        String id = request.get("id");
        LossSaleAdjust lossSaleAdjust = SpringManager.getBean(LossSaleAdjustRepository.class).findOne(id);
        process(lossSaleAdjust);
    }


    public void process(LossSaleAdjust lossSaleAdjust) {
        // 退销售部分
        returnSaleProcess(lossSaleAdjust);

        // 重新销售部分
        reSaleProcess(lossSaleAdjust);
    }

    /**
     * 退销售部分
     *
     * @param lossSaleAdjust LossSaleAdjust
     */
    private void returnSaleProcess(LossSaleAdjust lossSaleAdjust) {
        // 退回时判断老保险公司
        InsuranceRule insuranceRule = SpringManager.getBean(InsuranceRuleService.class)
                .findByDeptAndInsuranceCompany(lossSaleAdjust.getDept(), lossSaleAdjust.getOldInsuranceCompany());
        if (Objects.isNull(insuranceRule)) throw new RuntimeException("没有找到对应的车险规则，请检查！");

        // 获取保险公司客户
        InsuranceCompanyDetail companyDetail = SpringManager.getBean(FixedLossToForeign.class)
                .getInsuranceCompanyToClient(lossSaleAdjust.getOldInsuranceCompany(), lossSaleAdjust.getDept());

        if (insuranceRule.getCompany().getId().equals(lossSaleAdjust.getCompany().getId())) {
            /**  原单：直接对保险公司销售  **/
            returnInsuranceCompanySale(lossSaleAdjust, companyDetail.getClient());
        } else {
            /** 原单：找合同公司对保险公司销售  **/
            crossReturn(lossSaleAdjust, insuranceRule, companyDetail.getClient());
        }
    }

    private void crossReturn(LossSaleAdjust lossSaleAdjust, InsuranceRule insuranceRule, PartyRole insuranceCompany) {
        /* 合同公司：退对保险公司的销售，退定损公司的采购 */
        RetailOrder retailOrder = SpringManager.getBean(RetailOrderRepository.class).findOne(lossSaleAdjust.getRetailId());

        // 找原销售凭证
        Voucher originSaleVoucher = voucherRepository.findOne(
                        JpaDsl.toCriteriaByEq("originBusinessType", retailOrder.getInnerBusinessType(),
                                "origin", retailOrder.getId(), "voucherType", "CUSTOMER_SALE",
                                "partner", insuranceCompany.getParty().getPartyRoleByType("Customer")))
                .orElseThrow(() -> new RuntimeException("开票方生成对保险公司销售退回凭证时，未找到：" + retailOrder.getId() + " 的销售凭证！"));
        // 退销售凭证
        returnSaleVoucher(originSaleVoucher, lossSaleAdjust);
        // 退采购凭证
        returnPurchaseVoucher(originSaleVoucher, lossSaleAdjust, retailOrder.getServiceDept().getCompany());

        // 定损公司：退对合同公式的销售
        returnInsuranceCompanySale(lossSaleAdjust, insuranceRule.getCompany());
    }

    private void returnInsuranceCompanySale(LossSaleAdjust lossSaleAdjust, PartyRole insuranceCompany) {
        List<SaleOrder> saleOrders = saleOrderRepository.findAllBySource(lossSaleAdjust.getRetailId());
        if (saleOrders.isEmpty())
            throw new RuntimeException("生成退回凭证时，根据零售单【" + lossSaleAdjust.getRetailId() + "】未找到分销单号");

        saleOrders.forEach(saleOrder -> {
            // 找原销售凭证
            Voucher originSaleVoucher = voucherRepository.findOne(
                            JpaDsl.toCriteriaByEq("sourceBusinessType", saleOrder.getInnerBusinessType(),
                                    "source", saleOrder.getId(), "voucherType", "CUSTOMER_SALE",
                                    "partner", insuranceCompany.getParty().getPartyRoleByType("Customer")))
                    .orElseThrow(() -> new RuntimeException("生成对保险公司销售退回凭证时，未找到：" + saleOrder.getId() + " 的销售凭证！"));

            // 生成销售退回凭证
            returnSaleVoucher(originSaleVoucher, lossSaleAdjust);
        });
    }

    private Voucher returnSaleVoucher(Voucher originSaleVoucher, LossSaleAdjust lossSaleAdjust) {
        Voucher voucher = new Voucher();
        // 销售退回必填字段
        voucher.setReturned(true);
        voucher.setBusinessType(BusinessType.SALE_RETURN);
        voucher.setVoucher(originSaleVoucher);

        ReflectUtil.copy(originSaleVoucher, voucher, "dept", "company", "partner");
        voucher.setInventoryMode(InventoryMode.IM_OWN);     //设置库存模式：自有经销
        voucher.setAccountingMethod(AccountingMethod.AM_PURCHASE);  // 核算方式：采购
        voucher.setClassification(VoucherType.LOSS_SALE_RETURN);

        voucher.setCustomerExpand(lossSaleAdjust.getCustomerExpand().getId());
        voucher.setScene(lossSaleAdjust.getScene());
        voucher.setOrigin(lossSaleAdjust.getLossQuotationId());
        voucher.setOriginBusinessType(LossQuotation.class.getSimpleName());
        voucher.setSource(lossSaleAdjust.getId());
        voucher.setSourceBusinessType(lossSaleAdjust.getInnerBusinessType());

        voucher.setVoucherItems(
                originSaleVoucher.getVoucherItems().stream().map(saleItem -> {
                    VoucherItem voucherItem = new VoucherItem();

                    voucherItem.setProduct(saleItem.getProduct());
                    voucherItem.setTax(saleItem.getTax());
                    voucherItem.setOwner(saleItem.obtainOwner());
                    voucherItem.setUom(saleItem.getUom());          // 设置为销售的单位以及数量
                    voucherItem.setProductQty(-1 * Math.abs(saleItem.getProductQty()));
                    voucherItem.setPriceUnit(saleItem.getPriceUnit());      // 默认为退货单价
                    voucherItem.setOriginItemId(String.valueOf(saleItem.getId()));
                    voucherItem.setSourceItemId(String.valueOf(saleItem.getId()));
                    voucherItem.setOwner(originSaleVoucher.getCompany());

                    voucherItem.setVoucherItem(saleItem);   // 退回时传入明细行号

                    return voucherItem;
                }).collect(Collectors.toSet()));

        voucher = invoke("voucher.create", MapUtil.mapped(voucher));
        invoke("invoice.create", MapUtil.mapped(invoke("voucher.flow.invoice", MapUtil.mapped(voucher))));
        return voucher;
    }

    private void returnPurchaseVoucher(Voucher originSaleVoucher, LossSaleAdjust lossSaleAdjust, PartyRole supplier) {
        Voucher voucher = new Voucher();
        voucher.setReturned(true);
        voucher.setBusinessType(BusinessType.PURCHASE_RETURN);

        ReflectUtil.copy(originSaleVoucher, voucher, "dept", "company");
        voucher.setPartner(supplier.getParty().getPartyRoleByType("Supplier"));
        voucher.setClerk(originSaleVoucher.getClerk());

        voucher.setInventoryMode(InventoryMode.IM_OWN);     //设置库存模式：自有经销
        voucher.setAccountingMethod(AccountingMethod.AM_PURCHASE);  // 核算方式：采购

        voucher.setCustomerExpand(lossSaleAdjust.getCustomerExpand().getId());
        voucher.setScene(lossSaleAdjust.getScene());
        voucher.setOrigin(lossSaleAdjust.getLossQuotationId());
        voucher.setOriginBusinessType(LossQuotation.class.getSimpleName());
        voucher.setSource(lossSaleAdjust.getId());
        voucher.setSourceBusinessType(lossSaleAdjust.getInnerBusinessType());

        voucher.setVoucherItems(
                originSaleVoucher.getVoucherItems().stream().map(saleVoucherItem -> {
                    VoucherItem voucherItem = new VoucherItem();

                    voucherItem.setProduct(saleVoucherItem.getProduct());
                    voucherItem.setUom(saleVoucherItem.getUom());
                    voucherItem.setProductQty(-1.00 * saleVoucherItem.getProductQty());
                    voucherItem.setOriginItemId(String.valueOf(saleVoucherItem.getId()));
                    voucherItem.setSourceItemId(String.valueOf(saleVoucherItem.getId()));
                    voucherItem.setOwner(originSaleVoucher.getCompany());

                    // 获取原销售批次
                    VoucherInventory voucherInventory = voucherInventoryRepository.findAll(
                                    JpaDsl.toCriteriaByEq("voucher.id", originSaleVoucher.getId(), "voucherItem", saleVoucherItem))
                            .stream().findFirst().get();

                    InventoryBatch inventoryBatch = voucherInventory.getInventoryBatch();
                    voucherItem.setInventoryBatch(inventoryBatch);
                    voucherItem.setPriceUnit(inventoryBatch.getTaxCostPrice());
                    voucherItem.setSupplier(inventoryBatch.getSupplier());
                    voucherItem.setTax(inventoryBatch.getTax());

                    //  增加冻结数量
                    inventoryService.changeFrostQty(inventoryBatch, saleVoucherItem.getUom(), saleVoucherItem.getProductQty(), inventoryBatch.getCompany(),
                            lossSaleAdjust.getInnerBusinessType(), lossSaleAdjust.getId(), String.valueOf(saleVoucherItem.getId()), "定损调整销售退回");

                    return voucherItem;
                }).collect(Collectors.toSet()));

        Voucher newVoucher = invoke("voucher.create", MapUtil.mapped(voucher));
        invoke("invoice.create", MapUtil.mapped(invoke("voucher.flow.invoice", MapUtil.mapped(newVoucher))));
    }

    /**
     * 重新销售
     *
     * @param lossSaleAdjust LossSaleAdjust
     */
    private void reSaleProcess(LossSaleAdjust lossSaleAdjust) {
        // 再次销售时判断新保险公司
        InsuranceRule insuranceRule = SpringManager.getBean(InsuranceRuleService.class)
                .findByDeptAndInsuranceCompany(lossSaleAdjust.getDept(), lossSaleAdjust.getNewInsuranceCompany());
        if (insuranceRule == null)
            throw new RuntimeException("没有找到" + lossSaleAdjust.getNewInsuranceCompany().getName() + "的门店定损规则");

        // 获取保险公司客户
        InsuranceCompanyDetail companyDetail = SpringManager.getBean(FixedLossToForeign.class)
                .getInsuranceCompanyToClient(lossSaleAdjust.getNewInsuranceCompany(), lossSaleAdjust.getDept());

        if (insuranceRule.getCompany().getId().equals(lossSaleAdjust.getCompany().getId())) {
            //  直接对保险公司销售
            createInsuranceCompanySale(lossSaleAdjust, companyDetail.getClient());
        } else {
            /** 找合同公司对保险公司销售  **/
            crossSale(lossSaleAdjust, insuranceRule, companyDetail.getClient());
        }
    }

    private void crossSale(LossSaleAdjust lossSaleAdjust, InsuranceRule insuranceRule, PartyRole insuranceCompany) {
        // 定损方卖给合同公司
        List<SaleOrder> saleOrders = saleOrderRepository.findAllBySource(lossSaleAdjust.getRetailId());
        if (saleOrders.isEmpty())
            throw new RuntimeException("生成退回凭证时，根据零售单【" + lossSaleAdjust.getRetailId() + "】未找到分销单号");

        saleOrders.forEach(saleOrder -> createInsuranceCompanySaleVoucher(saleOrder, lossSaleAdjust, insuranceRule.getCompany()));

        // 合同公司采购、卖给保险公司
        RetailOrder retailOrder = SpringManager.getBean(RetailOrderRepository.class).findOne(lossSaleAdjust.getRetailId());
        Voucher voucher = createBasePurchase(retailOrder, insuranceRule.getCompany(), lossSaleAdjust);
        createBaseInsuranceSale(voucher, retailOrder, lossSaleAdjust, insuranceCompany);
    }

    private void createInsuranceCompanySale(LossSaleAdjust lossSaleAdjust, PartyRole insuranceCompany) {
        List<SaleOrder> saleOrders = saleOrderRepository.findAllBySource(lossSaleAdjust.getRetailId());
        if (saleOrders.isEmpty())
            throw new RuntimeException("生成退回凭证时，根据零售单【" + lossSaleAdjust.getRetailId() + "】未找到分销单号");

        saleOrders.forEach(saleOrder -> createInsuranceCompanySaleVoucher(saleOrder, lossSaleAdjust, insuranceCompany));
    }

    private void createInsuranceCompanySaleVoucher(SaleOrder saleOrder, LossSaleAdjust lossSaleAdjust, PartyRole customer) {
        Voucher voucher = new Voucher();
        voucher.setBusinessType(BusinessType.SALE_ORDER);

        voucher.setDept(saleOrder.getDept());
        voucher.setCompany(saleOrder.getCompany());
        voucher.setPartner(customer.getParty().getPatyRoleByType("Customer"));
        voucher.setCustomerExpand(saleOrder.getFnCustomerExpandId());
        voucher.setClassification(VoucherType.LOSS_SALE_RETURN);

        voucher.setClerk(saleOrder.getClerk()); //业务员
        voucher.setScene(lossSaleAdjust.getScene());
        voucher.setOrigin(lossSaleAdjust.getLossQuotationId());
        voucher.setOriginBusinessType(LossQuotation.class.getSimpleName());
        voucher.setSource(lossSaleAdjust.getId());
        voucher.setSourceBusinessType(lossSaleAdjust.getInnerBusinessType());

        voucher.setInventoryMode(InventoryMode.IM_OWN);     //设置库存模式：自有经销
        voucher.setAccountingMethod(AccountingMethod.AM_PURCHASE);  // 核算方式：采购

        voucher.setVoucherItems(
                saleOrder.getSaleItems().stream().map(
                        item -> {
                            VoucherItem voucherItem = new VoucherItem();

                            voucherItem.setProduct(item.getProduct());
                            voucherItem.setOwner(saleOrder.getCompany());
                            voucherItem.setUom(item.getUom());
                            voucherItem.setTax(item.getTax());

                            voucherItem.setProductQty(item.getProductUomQty());
                            voucherItem.setPriceUnit(item.getPriceUnit());
                            voucherItem.setPriceTotal(item.getPriceTotal());

                            voucherItem.setOriginItemId(item.getOriginItemId());
                            voucherItem.setSourceItemId(item.getId());
                            voucherItem.setSupplier(item.getSupplier());

                            voucherItem.setInventoryBatch(item.getInventoryBatch());

                            if (item.getInventoryBatch() != null) {
                                // 冻结批次
                                inventoryService.changeFrostQty(item.getInventoryBatch(), item.getUom(), item.getProductUomQty(), item.getCompany(),
                                        saleOrder.getInnerBusinessType(), saleOrder.getId(), String.valueOf(item.getId()), "定损调整销售重新生成对保险公司销售");
                            }

                            return voucherItem;
                        }).collect(Collectors.toSet())
        );
        voucher = invoke("voucher.create", MapUtil.mapped(voucher));
        invoke("invoice.create", MapUtil.mapped(invoke("voucher.flow.invoice", MapUtil.mapped(voucher))));
    }


    private Voucher createBasePurchase(RetailOrder retailOrder, PartyRole insuranceSaleCompany, LossSaleAdjust lossSaleAdjust) {
        // 生成带安采购凭证
        Voucher purVoucher = new Voucher();
        purVoucher.setBusinessType(BusinessType.PURCHASE_ORDER);

        PartyRole company = insuranceSaleCompany.getParty().getPartyRoleByType("Company");
        purVoucher.setCompany(company);
        purVoucher.setDept(getDept(purVoucher.getCompany()));     // 采购部门
        purVoucher.setPartner(retailOrder.getServiceDept().getCompany().getParty().getPatyRoleByType("Supplier"));

        purVoucher.setInventoryMode(InventoryMode.IM_OWN);     //设置库存模式：自有经销
        purVoucher.setAccountingMethod(AccountingMethod.AM_PURCHASE);  // 核算方式：采购

        purVoucher.setCustomerExpand(retailOrder.getCustomerExpand().getId());
        purVoucher.setClerk(retailOrder.getClerk()); //业务员

        purVoucher.setOrigin(lossSaleAdjust.getLossQuotationId());
        purVoucher.setOriginBusinessType(LossQuotation.class.getSimpleName());
        purVoucher.setSource(lossSaleAdjust.getId());
        purVoucher.setSourceBusinessType(lossSaleAdjust.getInnerBusinessType());

        PartyRole supplier = purVoucher.getPartner();
        purVoucher.setVoucherItems(
                retailOrder.getSaleItems().stream().map(
                        item -> {
                            VoucherItem voucherItem = new VoucherItem();
                            ReflectUtil.copy(item, voucherItem, "product", "uom", "priceUnit");

                            voucherItem.setProductQty(item.getProductUomQty());
                            voucherItem.setOriginItemId(item.getId());
                            voucherItem.setSourceItemId(item.getId());
                            voucherItem.setOwner(company);
                            voucherItem.setDiscount(0d);
                            Tax tax = taxService.getPurchaseTax(company, supplier, item.getProduct());   //供应商进项税
                            voucherItem.setTax(tax);
                            voucherItem.setInnerSalePriceUnit(item.getInnerSalePriceUnit());    // 公司间交易价
                            return voucherItem;
                        }
                ).collect(Collectors.toSet()));
        purVoucher = invoke("voucher.create", MapUtil.mapped(purVoucher));

        changeFrostQty(purVoucher, "定损调整销售，基地对门店的采购凭证");

        //生成账单
        invoke("invoice.create", MapUtil.mapped(invoke("voucher.flow.invoice", MapUtil.mapped(purVoucher))));
        return purVoucher;
    }

    private void createBaseInsuranceSale(Voucher purVoucher, RetailOrder retailOrder, LossSaleAdjust lossSaleAdjust, PartyRole insuranceCompamy) {
        Voucher voucher = new Voucher();
        voucher.setBusinessType(BusinessType.SALE_ORDER);

        ReflectUtil.copy(purVoucher, voucher, "dept", "company", "clerk", "customerExpand");
        voucher.setPartner(insuranceCompamy);

        voucher.setInventoryMode(InventoryMode.IM_OWN);     //设置库存模式：自有经销
        voucher.setAccountingMethod(AccountingMethod.AM_PURCHASE);  // 核算方式：采购
        voucher.setCustomerExpand(retailOrder.getCustomerExpand().getId());
        voucher.setClassification(VoucherType.LOSS_SALE_RETURN);

        voucher.setOrigin(lossSaleAdjust.getLossQuotationId());
        voucher.setOriginBusinessType(LossQuotation.class.getSimpleName());
        voucher.setSource(lossSaleAdjust.getId());
        voucher.setSourceBusinessType(lossSaleAdjust.getInnerBusinessType());

        voucher.setVoucherItems(
                retailOrder.getSaleItems().stream().map(
                        retailOrderItem -> {
                            VoucherItem voucherItem = new VoucherItem();
                            ReflectUtil.copy(retailOrderItem, voucherItem, "product", "uom", "priceUnit");

                            voucherItem.setProductQty(retailOrderItem.getProductUomQty());
                            voucherItem.setOriginItemId(retailOrderItem.getId());
                            voucherItem.setSourceItemId(retailOrderItem.getId());
                            voucherItem.setDiscount(0d);

                            Tax tax = taxService.getSaleTax(purVoucher.getCompany(), retailOrderItem.getProduct());
                            voucherItem.setTax(tax);

                            voucherItem.setOwner(purVoucher.getCompany());
                            return voucherItem;
                        }).collect(Collectors.toSet())
        );
        double lossQuotationFeeAmt = voucher.getVoucherItems().stream().mapToDouble(VoucherItem::getLossQuotationFee).sum();
        voucher.setLossQuotationFeeAmt(lossQuotationFeeAmt);

        // 添加采购凭证批次以及供应商信息
        voucher.getVoucherItems().forEach(item -> {
            purVoucher.getVoucherItems().forEach(purchaseVoucherItem -> {

                VoucherInventory voucherInventory = voucherInventoryRepository.findByVoucherItem(purchaseVoucherItem).stream()
                        .findAny().orElseThrow(() -> new RuntimeException("找不到批次信息！"));

                if (item.getProduct().getId().equals(voucherInventory.getProduct().getId())) {
                    item.setInventoryBatch(voucherInventory.getInventoryBatch());
                    item.setSupplier(purchaseVoucherItem.getSupplier());
                }
            });
        });

        // 创建账单
        voucher = invoke("voucher.create", MapUtil.mapped(voucher));

        //增加账单
        invoke("invoice.create", MapUtil.mapped(invoke("voucher.flow.invoice", MapUtil.mapped(voucher))));
    }

    private Dept getDept(PartyRole company) {
        return SpringManager.getBean(DeptRepository.class)
                .findAll(JpaDsl.toCriteriaByEq("company", company, "innerTag", true, "deptType.id", "000006"))
                .stream().findFirst().orElseThrow(() -> new RuntimeException("没有找到内定标记部门，请先定义！公司：：" + company.getId()));
    }

    private void changeFrostQty(Voucher voucher, String remarks) {
        voucher.getVoucherItems().forEach(
                voucherItem -> {
                    VoucherInventory voucherInventory = voucherInventoryRepository.findByVoucherItem(voucherItem)
                            .stream().findAny().orElseThrow(() -> new RuntimeException("找不到批次信息！"));
                    InventoryBatch inventoryBatch = voucherInventory.getInventoryBatch();

                    // 冻结批次数量
                    inventoryService.changeFrostQty(inventoryBatch, inventoryBatch.getUom(), inventoryBatch.getPurchaseQty(),
                            voucher.getCompany(), voucher.getInnerBusinessType(), voucher.getId(),
                            String.valueOf(voucherItem.getId()), remarks);
                });
    }
}
